#ifndef __M_SS_H__
#define __M_SS_H__
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include "util.hpp"
typedef enum{UNLOGIN,LOGIN} ss_statu;
typedef websocketpp::server<websocketpp::config::asio> wsserver_t;


/*session类*/
class session{
private:
    uint64_t _ssid;//标识符
    uint64_t _uid;//session对应的用户id
    ss_statu _statu;//用户状态
    wsserver_t::timer_ptr _tp;//session关联的定时器
public:
    session(uint64_t ssid)
        :_ssid(ssid)
    {
        DLOG("SESSION %p 被创建！！",this);
    } 
    ~session(){DLOG("SESSION %p 被释放！！",this);}
    uint64_t ssid()
    {
        return _ssid;
    }
    void set_statu(ss_statu statu)
    {
        _statu=statu;
    }

    void set_user(uint64_t uid)
    {
        _uid=uid;
    }
    uint64_t get_user(){return _uid;}
    bool is_login(){return (_statu==LOGIN);}
    void set_timer(const wsserver_t::timer_ptr& tp)
    {
        _tp=tp;
    }
    wsserver_t::timer_ptr& get_timer(){return _tp;}
};


#define SESSION_TIMEOUT 30000 //30s
#define SESSION_FOREVER -1    //永久
using session_ptr=std::shared_ptr<session>;
/*session管理类*/
class session_manager{
private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t,session_ptr> _session;
    wsserver_t* _server;
public:
    session_manager(wsserver_t* srv)
        :_next_ssid(1),_server(srv)
    {
        DLOG("session管理器初始化完毕");
    }
    ~session_manager(){
        DLOG("session管理器即将销毁");
    }
    session_ptr create_session(uint64_t uid,ss_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));//创建智能指针对象
        ssp->set_statu(statu);//设置状态
        ssp->set_user(uid);
        _session.insert(std::make_pair(_next_ssid,ssp));//添加到映射表
        _next_ssid++;//标识符++
        return ssp;
    }
    //在session管理器中添加session
    void append_session(const session_ptr&ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->ssid(),ssp));
    }

    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it=_session.find(ssid);
        if(it==_session.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    
    /*通过ssid删除session*/
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
       _session.erase(ssid);
    }

    /// @brief 设置定时器，session有效时间
    /// @param ssid 
    /// @param ms 
    void set_session_exprie_time(uint64_t ssid,int ms)
    {
        //依赖于websocketpp的定时器来完成session生命周期的管理
        //登录之后，创建session，session在指定时间内，如果无通信，就删除
        //进入游戏大厅或者游戏房间：session应该永久存在
        //退出之后，session应该重新被设置为临时，然后无通信后删除
        session_ptr ssp=get_session_by_ssid(ssid);

        //没有这个会话，不需要设置
        if(ssp.get()==nullptr){
            return;
        }
        wsserver_t::timer_ptr tp=ssp->get_timer();//获取定时器
        //定时器为空代表永久存在
        if(tp.get()==nullptr && ms==SESSION_FOREVER)
        {
            //1.在session永久保存的情况下，设置永久存在
            return;
        }
        //永久存在的情况下设置一段时间后删除
        else if(tp.get()==nullptr && ms!=SESSION_FOREVER)
        {
            //2.在session永久存在的情况下，设置指定时间之后被删除的定时任务
            wsserver_t::timer_ptr tmp_tp=_server->set_timer(
                ms,std::bind(&session_manager::remove_session,this,ssid)
            );

            ssp->set_timer(tmp_tp);
        }
        
        else if(tp.get()!=nullptr&& ms==SESSION_FOREVER)
        {
            //3.在session设置了定时删除的情况下，将session设置为永久存在
            //删除定时任务，stready_timer删除定时任务会导致任务直接执行，执行就会删除ssp
            tp->cancel();//取消定时任务,但不是立即取消的
            ssp->set_timer(wsserver_t::timer_ptr());//session关联的定时任务设置为空

            //避免添加之后才去取消定时任务
            //取消定时任务时会从session管理器中移除，重新给session管理器添加信息
            //使用websocketpp提供的定时器，0s后执行一个添加任务
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));

        }
        else if(tp.get()!=nullptr&& ms!=SESSION_FOREVER)
        {
            //4.在session设置了定时删除的情况下，将session重置删除时间
            tp->cancel();//取消定时任务,但不是立即取消的，执行之后删除ssp
            //取消之后立即添加一个session
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
            //重新设置定时任务
            ssp->set_timer(wsserver_t::timer_ptr());

            //重新设置定时任务，在指定时间后删除 
            wsserver_t::timer_ptr tmp_tp=_server->set_timer(
                ms,std::bind(&session_manager::remove_session,this,ssp->ssid())
            );
            //重新设置session关联的定时器
            ssp->set_timer(tmp_tp);
        }


    }
};
#endif